package org.simpleFramework.mvc.processor.impl;

import lombok.extern.slf4j.Slf4j;
import org.simpleFramework.core.BeanContainer;
import org.simpleFramework.mvc.RequestProcessorChain;
import org.simpleFramework.mvc.annotation.RequestMapping;
import org.simpleFramework.mvc.annotation.RequestParam;
import org.simpleFramework.mvc.annotation.ResponseBody;
import org.simpleFramework.mvc.processor.RequestProcessor;
import org.simpleFramework.mvc.render.ResultRender;
import org.simpleFramework.mvc.render.impl.JsonResultRender;
import org.simpleFramework.mvc.render.impl.ResourceNotFoundResultRender;
import org.simpleFramework.mvc.render.impl.ViewResultRender;
import org.simpleFramework.mvc.type.ControllerMethod;
import org.simpleFramework.mvc.type.RequestPathInfo;
import org.simpleFramework.util.ConverterUtil;
import org.simpleFramework.util.ValidationUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @description:
 * @author: air
 * @create: 2020-05-14 14:39
 */
@Slf4j
public class ControllerRequestProcessor implements RequestProcessor {
    private BeanContainer beanContainer;

    private Map<RequestPathInfo, ControllerMethod> pathInfoControllerMethodMap=new ConcurrentHashMap<>();

    public ControllerRequestProcessor() {
        this.beanContainer=BeanContainer.getInstance();

        Set<Class<?>> requestMappingSet= beanContainer.getClassesByAnnotation(RequestMapping.class);
        initPathControllerMethodMap(requestMappingSet);

    }

    private void initPathControllerMethodMap(Set<Class<?>> requestMappingSet) {
        if (ValidationUtil.isEmpty(requestMappingSet)){
            return;
        }
        for (Class<?> requestMappingClass :requestMappingSet){
            RequestMapping requestMapping=requestMappingClass.getAnnotation(RequestMapping.class);
            String basePath=requestMapping.value();
            if(!basePath.startsWith("/")){
                basePath="/"+basePath;
            }
            Method[] methods=requestMappingClass.getDeclaredMethods();
            if (ValidationUtil.isEmpty(methods)){
                continue;
            }
            for (Method method:methods){
                if(method.isAnnotationPresent(RequestMapping.class)){
                    RequestMapping methodRequest=method.getAnnotation(RequestMapping.class);
                    String methodPath=methodRequest.value();
                    if (!methodPath.startsWith("/")){
                        methodPath="/"+methodPath;
                    }
                    String url=basePath+methodPath;

                    Map<String,Class<?>> methodParams=new HashMap<>();
                    Parameter[] parameters=method.getParameters();
                    if(!ValidationUtil.isEmpty(parameters)){
                        for (Parameter parameter:parameters){
                            RequestParam param=parameter.getAnnotation(RequestParam.class);
                            if (param==null){
                                // 暂定为Controller方法里面所有的参数都需要@RequestParams注解
                                throw new RuntimeException("the parameter must have @RequestParam");
                            }
                            methodParams.put(param.value(),parameter.getType());
                        }

                        String httpMethod= String.valueOf(methodRequest.method());
                        RequestPathInfo requestPathInfo=new RequestPathInfo(httpMethod,url);
                        if (this.pathInfoControllerMethodMap.containsKey(requestPathInfo)){
                            // 请求路径重复
                            log.warn("duplicate url:{} registration,current class {} method {} will override thr former one",
                                    requestPathInfo.getHttpPath(),requestMappingClass.getName(),method.getName());
                        }
                        ControllerMethod controllerMethod=new ControllerMethod(requestMappingClass,method,methodParams);
                        this.pathInfoControllerMethodMap.put(requestPathInfo,controllerMethod);
                    }

                }
            }
        }
    }

    @Override
    public boolean process(RequestProcessorChain requestProcessorChain) throws Exception {
        // 解析HttpServletRequest的请求方法，请求路径，获取对应的controllerMethods实例
        String method=requestProcessorChain.getRequestMethod();
        String path=requestProcessorChain.getRequestPath();
        ControllerMethod controllerMethod=this.pathInfoControllerMethodMap.get(new RequestPathInfo(method,path));
        if (controllerMethod==null){
            requestProcessorChain.setResultRender(new ResourceNotFoundResultRender(method,path));
            return false;
        }

        // 解析请求参数，并传递给获取到的controllerMethod实例去执行
        Object result=invokeControllerMethod(controllerMethod,requestProcessorChain.getRequest());

        // 根据解析的结果，选择对应的render进行渲染
        setResultRender(result,controllerMethod,requestProcessorChain);

        return true;
    }


    /**
     * 根据不同的情况设置不同的渲染器
     * @param result
     * @param controllerMethod
     * @param requestProcessorChain
     */
    private void setResultRender(Object result, ControllerMethod controllerMethod, RequestProcessorChain requestProcessorChain) {
        if (result==null){
            return;
        }
        ResultRender resultRender;
        boolean isJson=controllerMethod.getInvokeMethod().isAnnotationPresent(ResponseBody.class);
        if (isJson){
            resultRender=new JsonResultRender(result);
        }else {
            resultRender=new ViewResultRender(result);
        }

        requestProcessorChain.setResultRender(resultRender);

    }

    private Object invokeControllerMethod(ControllerMethod controllerMethod, HttpServletRequest request) {
        // 从请求里获取get或者post的参数及其对应的值
        Map<String,String> requestParamMap=new HashMap<>();
        Map<String,String[]> parameterMap=request.getParameterMap();
        for (Map.Entry<String,String[]>  parameter:parameterMap.entrySet()){
            if (!ValidationUtil.isEmpty(parameter.getValue())){
                requestParamMap.put(parameter.getKey(),parameter.getValue()[0]);
            }
        }

        // 实例化方法对应的参数
        List<Object> methodParams=new ArrayList<>();
        Map<String ,Class<?>> methodParameterMap=controllerMethod.getMethodParameters();
        for (String paramName : methodParameterMap.keySet()){
            Class<?> type=methodParameterMap.get(paramName);
            String requestValue= requestParamMap.get(paramName);
            Object value;
            // 只支持String 以及基础类型char，int,shot,byte,double,long,float,boolean，以及它们的包装类型
            if(requestValue==null){
                // 将请求参数值转成适配于参数类型的空值
                value= ConverterUtil.primitiveNull(type);
            }else {
                value=ConverterUtil.convert(type,requestValue);
            }
            methodParams.add(value);
        }

        // 执行controller里面对应的方法并返回结果
        Object controller =beanContainer.getBean(controllerMethod.getControllerClass());
        Method invokeMethod=controllerMethod.getInvokeMethod();
        invokeMethod.setAccessible(true);
        Object result = null;
        try{
            if(methodParams.size()==0){
                result=invokeMethod.invoke(controller);
            }else {
                result=invokeMethod.invoke(controller,methodParams.toArray());
            }

        }catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return result;
    }
}
